/* 
  请描述Vue2中响应式数据原理
    - Vue响应式指的是:组件的data数据发生改变,立刻就会触发视图的更新
    - 响应式使用的就是:发布-订阅模式(也称作观察者模式)主要是:数据劫持 + 依赖收集
    - 数据劫持的原理:
      Vue2通过Object.defineProperty来进行数据劫持,把data中每一个对象的属性使用defineProperty重写getter和setter,具体做法书写一个Observer类,其实就是发布订阅模式中的发布器,主要功能就是对数据进行劫持,并在getter和setter中完成功能,在getter中收集依赖,在setter中通知依赖进行视图更新
    - 依赖收集原理:
      - 有一个Dep类,其实就是发布订阅模型的订阅器,Dep上有收集依赖和通知依赖更新的方法,每一个数据在劫持的时候,创建一个dep实例,在getter中调用dep实例的收集依赖方法,在setter中调用dep实例的通知依赖更新方法
      - 有一个Watcher类,Wacther身上有获取数据的方法,和重新获取数据并更新视图的update方法.每次模板中编译data数据的时候,都会创建一个对应的watcher实例,wacther实例会在第一个获取时候的时候被收集到当前数据的dep中,在数据更新时,会通知所有watcher调用其身上的update方法,达到响应式的目的
*/
let W = null
// 6.定义一个Watcher类(发布-订阅中的订阅者)
class watcher {
  constructor(obj, key) {
    this.obj = obj
    this.key = key
    w = this
    this.get()
    W = null
  }
  //当组件内new Watcher的时候就是想得到一个值,所以封装一个get方法拿值
  get() {
    return this.obj[this.key]
  }
  //在watcher内部封装一个update方法,当通知我更新的时候,直接调用当前的update方法重新去获取数据然后重新渲染视图(Compiler)
  update() {
    console.log('更新了');
    this.get()
  }
}
// 5.定义一个Dep类(收集依赖 提示更新 中间过渡的)
class Dep {
  constructor() {
    // 5.1创建一个空的容器用来放watcher
    this.subs = []
  }
  // 5.2实例上放着两个方法 一个收集watcher 一个提示watcher 更新
  getDep() {
    // 收集watcher
    this.subs.push(W)
  }
  updatedDep() {
    // 容器里放着都是watcher 调用watcher里更新的方法
    this.subs.forEach(watcher => { watcher.update() })
  }
}
// 4.封装defineReactive函数(响应式操作)
function defineReactive(_data, key, value) {
  // 4.1再次深度遍历 
  observe(value)
  // 4.2每次对一个值进行响应式的时候实例化一个Dep类
  const dep = new Dep()
  // 4.3数据劫持(类似拦截器)
  Object.defineProperty(_data, key, {
    get() {
      // 如果是解析模板的时候获取值 dep需要保存依赖
      if (W) {
        dep.getDep(W)
      }
      return value
    },
    set(newVal) {
      value = newVal
      // 更新的时候dep需要提示watcher来拿值
      dep.updatedDep()
    }
  })
}
// 3.封装一个Observer类(发布订阅模型中的发布者--发布器)
class Observer {
  constructor(_data) {
    // 3.1把_data的值放在实例身上
    this._data = _data
    // 3.2对数据进行判断数组还是对象
    if (Array.isArray(_data)) {
      this.ObserverAry(_data)
    } else {
      this.ObserverObj(_data)
    }
  }
  //3.3 数组的处理方法
  ObserverAry(_data) {
    // 再次遍历 是为了遍历数组里是不是还有对象 对对象进行深度监听
    _data.forEach(item => observe(item))
  }
  // 3.3 对象的处理方法
  ObserverObj(_data) {
    // 遍历它 将对象 和对象的每个属性 和值都传过去
    for (let key of Object.keys(_data)) {
      defineReactive(_data, key, _data[key])
    }
  }
}
// 2.封装一个observe函数 接受_data的值进行数据判断类型
function observe(_data) {
  // 2.1进行判断类型,如果是对象就实例化Observer 并传入值
  if (typeof _data !== 'object' || _data === null) return
  // 2.2使用实例化Observer对数据进行响应式处理
  new Observer(_data)
}
// 1.封装一个Vue的构造函数 接受一个配置对象
function Vue(options) {
  // 1.1将配置对象内的data放在实例对象this的_data属性上 后续操作都是对实例身上的数据进行操作
  this._data = options.data
  for (let key of Object.keys(this._data)) {
    // 实例对象身上都放一个属性 就是this._data的属性
    Object.defineProperty(this, key, {
      get() {
        return this._data[key]
      },
      set(newVal) {
        this._data[key] = newVal
      }
    })
  }
  // 1.2 进行数据劫持
  observe(this._data)
}

// 创建一个实例化对象
const vm = new Vue({
  data: {
    count: 0,
    student: {
      name: '罗飞'
    },
    Ary: [
      { id: 1, type: '胡辣汤' },
      { id: 2, type: '热干面' },
      { id: 3, type: '馄饨' }
    ]
  }
})